package com.da.javatest.interview.session2;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author chenlida
 * @date 2020/3/30 23:48
 * 高内聚低耦合，多对一要加锁，线程操作资源类，判断干活通知
 * 多线程判断需要用while防止虚假唤醒
 *
 * sync 与 myLock 的区别：
 * 1.一个是JVM层面的一个是API层面的
 * 2.sync不可中断lock可以中断
 * 3.synchronized 可以给类、方法、代码块加锁；而 Lock 只能给代码块加锁。
 * 4.synchronized 不需要手动获取锁和释放锁，使用简单，发生异常会自动释放锁，不会造成死锁；
 * 而 Lock 需要自己加锁和释放锁，如果使用不当没有 unLock()去释放锁就会造成死锁。
 * 5.lock可以实现公平锁
 * 6.lock可以绑定多个条件(condition)，实现精确唤醒
 * 7.二者的锁机制其实也是不一样的。ReentrantLock底层调用的是Unsafe的park方法加锁，synchronized操作的应该是对象头中mark word
 *
 * Java中每一个对象都可以作为锁，这是synchronized实现同步的基础：
 * 普通同步方法，锁是当前实例对象
 * 静态同步方法，锁是当前类的class对象
 * 同步方法块，锁是括号里面的对象
 */
public class Test17 {
    private int num = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    private void increment() {
        try {
            lock.lock();
            // 判断
            while (num != 0) {
                condition.await();
            }
            // 干活
            num++;
            System.out.println(Thread.currentThread().getName() + " " + num);
            // 通知
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    private void decrement() {
        try {
            lock.lock();
            // 判断
            while (num == 0) {
                condition.await();
            }
            // 干活
            num--;
            System.out.println(Thread.currentThread().getName() + " " + num);
            // 通知
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        Test17 t = new Test17();
        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                t.increment();
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                t.decrement();
            }
        }, "B").start();
    }
}
